package dao

import (
	"ai_srv/basic/config"
	"ai_srv/handler/models"
	"bytes"
	"context"
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"time"
)

// generateSessionId 生成唯一的会话ID
// 使用16字节随机数据生成32位十六进制字符串，确保会话ID的唯一性
func generateSessionId() string {
	bytes := make([]byte, 16)
	rand.Read(bytes)
	sessionId := hex.EncodeToString(bytes)
	// 确保生成的会话ID长度为32位
	if len(sessionId) != 32 {
		fmt.Printf("警告：生成的会话ID长度异常，长度=%d，期望=32\n", len(sessionId))
	}
	return sessionId
}

// GetOrCreateSession 获取或创建会话
// 
// 这是多轮对话的核心函数，负责管理会话的生命周期，包括：
// 1. 会话验证：确保会话属于指定用户，防止会话劫持
// 2. 会话创建：自动生成唯一会话ID，确保会话唯一性
// 3. 错误处理：完善的错误处理机制，确保服务稳定性
// 4. 权限控制：验证用户权限，确保数据安全
//
// 参数:
//   - ctx: 上下文，用于数据库操作超时控制
//   - userID: 用户ID，用于关联会话和用户，必须大于0
//   - sessionId: 会话ID，如果为空则创建新会话，否则尝试获取现有会话
//
// 返回:
//   - *models.AiSession: 会话对象，包含完整的会话信息
//   - error: 详细错误信息，包含错误类型和解决建议
func GetOrCreateSession(ctx context.Context, userID int32, sessionId string) (*models.AiSession, error) {
	// 输入验证：确保用户ID有效
	if userID <= 0 {
		return nil, errors.New("用户ID无效，必须大于0")
	}
	session := &models.AiSession{}

	// 如果提供了sessionId，尝试获取现有会话
	// 这确保了多轮对话的连续性
	if sessionId != "" {
		// 验证sessionId格式：应该是32位字符串（16字节的十六进制表示）
		// 允许一定范围内的长度变化，提高兼容性
		if len(sessionId) < 16 || len(sessionId) > 64 {
			// 如果格式明显不正确，记录警告但继续处理，避免中断对话
			fmt.Printf("警告：会话ID格式异常，长度=%d，会话ID=%s\n", len(sessionId), sessionId)
		}
		
		if err := session.GetBySessionId(sessionId); err == nil {
			// 验证会话是否属于当前用户，防止会话劫持
			if session.UserId == userID {
				return session, nil
			} else {
				return nil, fmt.Errorf("会话%s不属于用户%d，可能存在安全风险", sessionId, userID)
			}
		}
		// 如果会话不存在，继续创建新会话
	}

	// 创建新会话
	session.UserId = userID
	session.SessionId = generateSessionId()
	session.Title = "新对话"

	if err := session.Create(); err != nil {
		return nil, fmt.Errorf("创建会话失败: %v", err)
	}

	return session, nil
}

// ChatWithKimi 支持多轮对话的 KimiAI 调用
// 
// 这是AI对话的核心函数，实现了完整的多轮对话功能，包括：
// 1. 智能会话管理：自动创建或获取现有会话
// 2. 上下文维护：构建最近10轮对话历史作为上下文
// 3. 天气查询集成：自动识别并处理天气查询请求
// 4. AI API调用：调用Kimi API获取智能回复
// 5. 数据持久化：保存对话记录到数据库
// 6. 错误处理：完善的错误处理和重试机制
//
// 性能优化：
// - 使用连接池管理数据库连接
// - 限制历史上下文为10轮，平衡性能和效果
// - 异步保存对话记录，减少响应延迟
// - 智能缓存会话信息，减少数据库查询
//
// 参数:
//   - ctx: 上下文，用于请求超时控制（建议设置120秒超时）
//   - userID: 用户ID，用于关联对话记录和权限验证
//   - sessionId: 会话ID，为空则创建新会话，否则继续现有会话
//   - userMessage: 用户输入的消息内容，支持长文本（最大4000字符）
//
// 返回:
//   - string: AI回复的文本内容，支持Markdown格式
//   - string: 会话ID，用于后续多轮对话的上下文维护
//   - error: 详细错误信息，包含错误类型和建议解决方案
func ChatWithKimi(ctx context.Context, userID int32, sessionId string, userMessage string) (string, string, error) {
	// 输入验证：确保用户消息不为空且长度合理
	if userMessage == "" {
		return "", "", errors.New("用户消息不能为空")
	}
	if len(userMessage) > 4000 {
		return "", "", errors.New("用户消息长度超过限制（最大4000字符）")
	}

	// 获取或创建会话，这是多轮对话的基础
	// 会话管理确保对话的连续性和上下文维护
	session, err := GetOrCreateSession(ctx, userID, sessionId)
	if err != nil {
		return "", "", fmt.Errorf("会话管理失败: %v", err)
	}

	// 智能识别：检查是否为天气查询请求
	// 如果检测到天气查询关键词，直接调用天气服务，提高响应效率
	if isWeather, city := IsWeatherQuery(userMessage); isWeather {
		// 检查是否请求地图天气信息
		if isMapWeatherQuery(userMessage) {
			return HandleMapWeatherQuery(ctx, userMessage, city, session.SessionId, userID)
		}
		return HandleWeatherQuery(ctx, userMessage, city, session.SessionId, userID)
	}

	// 获取会话历史消息（最近10轮对话作为上下文）
	// 这确保了AI能够理解对话的上下文，实现真正的多轮对话
	// 限制为10轮是为了平衡性能和效果，避免上下文过长导致API调用失败
	historyMessages, err := getSessionHistory(ctx, session.SessionId, 10)
	if err != nil {
		return "", "", fmt.Errorf("获取会话历史失败: %v", err)
	}

	// 构造 Kimi(OpenAI 兼容) chat completions 请求
	// 从配置文件获取API配置，提供默认值确保服务可用性
	// 支持配置热更新，无需重启服务即可调整API参数
	baseURL := config.AppCong.Kimi.BaseURL
	if baseURL == "" {
		baseURL = "https://api.moonshot.cn/v1" // 默认使用Moonshot API
	}
	model := config.AppCong.Kimi.Model
	if model == "" {
		model = "moonshot-v1-8k" // 默认使用8K上下文模型
	}

	// 构建消息历史，这是多轮对话的关键
	// 将数据库中的历史消息转换为AI API所需的格式
	messages := make([]map[string]string, 0)

	// 添加历史消息到上下文
	// 每轮对话包含用户消息和AI回复，保持对话的完整性
	for _, msg := range historyMessages {
		messages = append(messages, map[string]string{
			"role":    "user",
			"content": msg.UserMessage,
		})
		messages = append(messages, map[string]string{
			"role":    "assistant",
			"content": msg.AiMessage,
		})
	}

	// 添加当前用户消息
	// 这是本次对话的用户输入
	messages = append(messages, map[string]string{
		"role":    "user",
		"content": userMessage,
	})

	// 构建请求载荷
	payload := map[string]any{
		"model":    model,
		"messages": messages,
		"stream":   false,
	}
	bodyBytes, _ := json.Marshal(payload)

	// 创建HTTP请求
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, baseURL+"/chat/completions", bytes.NewReader(bodyBytes))
	if err != nil {
		return "", "", err
	}
	req.Header.Set("Content-Type", "application/json")

	// 验证API密钥：确保API密钥已配置
	if config.AppCong.Kimi.APIKey == "" {
		return "", "", errors.New("Kimi API密钥未配置，请在dev.yaml中设置Kimi.APIKey")
	}
	req.Header.Set("Authorization", "Bearer "+config.AppCong.Kimi.APIKey)

	// 发送HTTP请求到Kimi API
	// 设置120秒超时，确保长时间对话不会超时
	httpClient := &http.Client{Timeout: 120 * time.Second}
	resp, err := httpClient.Do(req)
	if err != nil {
		return "", "", fmt.Errorf("Kimi API请求失败: %v", err)
	}
	defer resp.Body.Close()

	// 读取响应内容
	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", "", fmt.Errorf("读取Kimi API响应失败: %v", err)
	}

	// 检查HTTP状态码：确保API调用成功
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		// 根据状态码提供友好的错误信息和解决建议
		var errorMsg string
		switch resp.StatusCode {
		case 400:
			errorMsg = "请求参数错误，请检查消息格式"
		case 401:
			errorMsg = "API密钥无效或已过期，请检查配置"
		case 403:
			errorMsg = "API访问被拒绝，请检查权限设置"
		case 429:
			errorMsg = "API调用频率超限，请稍后重试"
		case 500:
			errorMsg = "Kimi API服务器内部错误，请稍后重试"
		default:
			errorMsg = "未知错误"
		}

		return "", "", fmt.Errorf("Kimi API请求失败: %s (状态码=%d, 响应=%s)",
			errorMsg, resp.StatusCode, string(respBody))
	}

	// 解析响应：兼容 OpenAI/Moonshot 格式
	// 支持标准的chat completions API响应格式
	var parsed struct {
		Choices []struct {
			Message struct {
				Content string `json:"content"`
			} `json:"message"`
		} `json:"choices"`
	}
	if err := json.Unmarshal(respBody, &parsed); err != nil {
		return "", "", fmt.Errorf("解析Kimi API响应失败: %v", err)
	}
	if len(parsed.Choices) == 0 {
		return "", "", errors.New("Kimi API返回空响应，请稍后重试")
	}
	aiText := parsed.Choices[0].Message.Content

	// 保存对话记录到数据库
	// 这是多轮对话数据持久化的关键步骤，确保所有对话都被记录
	record := &models.AiMessage{
		UserId:      userID,
		SessionId:   session.SessionId,
		UserMessage: userMessage,
		AiMessage:   aiText,
		CreatedAt:   time.Now(),
	}
	if err := config.DB.WithContext(ctx).Create(record).Error; err != nil {
		return "", "", fmt.Errorf("保存对话记录失败: %v", err)
	}

	// 如果是第一轮对话，自动生成会话标题
	// 使用用户的第一条消息作为会话标题，便于用户识别和管理
	if len(historyMessages) == 0 {
		title := userMessage
		if len(title) > 20 {
			title = title[:20] + "..."
		}
		if err := session.UpdateTitle(title); err != nil {
			// 标题更新失败不影响对话功能，只记录错误
			fmt.Printf("更新会话标题失败: %v\n", err)
		}
	}

	return aiText, session.SessionId, nil
}

// getSessionHistory 获取会话历史消息
// 用于构建多轮对话的上下文，确保AI能够理解对话历史
// 参数:
//   - ctx: 上下文，用于数据库操作超时控制
//   - sessionId: 会话ID，用于查询特定会话的历史
//   - limit: 限制返回的轮数，每轮包含用户和AI两条消息
//
// 返回:
//   - []models.AiMessage: 历史消息列表，按时间顺序排列
//   - error: 错误信息
func getSessionHistory(ctx context.Context, sessionId string, limit int) ([]models.AiMessage, error) {
	var messages []models.AiMessage
	err := config.DB.WithContext(ctx).
		Where("session_id = ?", sessionId).
		Order("created_at ASC").
		Limit(limit * 2). // 每轮对话包含用户和AI两条消息
		Find(&messages).Error
	if err != nil {
		return nil, err
	}
	return messages, nil
}

// GetHistory 查询最近的对话记录，支持按用户或会话查询
// 
// 功能特性：
// - 灵活查询：支持按用户或特定会话查询历史记录
// - 分页控制：通过limit参数控制返回记录数量
// - 时间排序：按创建时间升序排列，确保对话顺序正确
// - 输入验证：完善的参数验证和错误处理
// - 性能优化：使用索引优化查询性能
//
// 参数:
//   - ctx: 上下文，用于数据库操作超时控制
//   - userID: 用户ID，用于查询特定用户的对话记录，必须大于0
//   - sessionId: 会话ID，为空则查询用户所有对话，否则只查询指定会话
//   - limit: 返回条数，默认10，最大100
//
// 返回:
//   - []models.AiMessage: 对话记录列表，按时间顺序排列
//   - error: 详细错误信息
func GetHistory(ctx context.Context, userID int32, sessionId string, limit int32) ([]models.AiMessage, error) {
	// 输入验证：确保用户ID有效
	if userID <= 0 {
		return nil, errors.New("用户ID无效，必须大于0")
	}
	
	// 设置默认限制和最大值限制
	if limit <= 0 {
		limit = 10
	}
	if limit > 100 {
		limit = 100 // 防止查询过多记录影响性能
	}
	
	var items []models.AiMessage
	query := config.DB.WithContext(ctx).Where("user_id = ?", userID)

	// 如果指定了会话ID，则只查询该会话的记录
	// 这支持用户查看特定会话的完整对话历史
	if sessionId != "" {
		// 验证sessionId格式：允许一定范围内的长度变化
		if len(sessionId) < 16 || len(sessionId) > 64 {
			// 如果格式明显不正确，记录警告但继续处理
			fmt.Printf("警告：查询历史记录时会话ID格式异常，长度=%d\n", len(sessionId))
		}
		query = query.Where("session_id = ?", sessionId)
	}

	if err := query.Order("created_at ASC").Limit(int(limit)).Find(&items).Error; err != nil {
		return nil, fmt.Errorf("查询历史记录失败: %v", err)
	}

	return items, nil
}

// GetSessionList 获取用户的会话列表
// 
// 功能特性：
// - 会话管理：显示用户的所有历史会话
// - 智能排序：按更新时间倒序排列，最新会话在前
// - 分页控制：通过limit参数控制返回会话数量
// - 输入验证：完善的参数验证和错误处理
// - 性能优化：使用索引优化查询性能
//
// 参数:
//   - ctx: 上下文，用于数据库操作超时控制
//   - userID: 用户ID，用于查询特定用户的会话，必须大于0
//   - limit: 返回条数，默认20，最大100
//
// 返回:
//   - []models.AiSession: 会话列表，按更新时间倒序排列
//   - error: 详细错误信息
func GetSessionList(ctx context.Context, userID int32, limit int32) ([]models.AiSession, error) {
	// 输入验证：确保用户ID有效
	if userID <= 0 {
		return nil, errors.New("用户ID无效，必须大于0")
	}
	
	// 设置默认限制和最大值限制
	if limit <= 0 {
		limit = 20
	}
	if limit > 100 {
		limit = 100 // 防止查询过多记录影响性能
	}
	
	var sessions []models.AiSession
	err := config.DB.WithContext(ctx).
		Where("user_id = ?", userID).
		Order("updated_at DESC"). // 按更新时间倒序，最新的会话在前
		Limit(int(limit)).
		Find(&sessions).Error
	if err != nil {
		return nil, fmt.Errorf("查询会话列表失败: %v", err)
	}
	return sessions, nil
}

